#include <net-snmp/net-snmp-config.h>
#include <net-snmp/net-snmp-includes.h>
#include <net-snmp/agent/net-snmp-agent-includes.h>
#include "snmprdbms.h"
#include "snmprdbms_ds.h"
#include "snmprdbms_config.h"



//============== TYPES ===================

//Get conf_type* by name or NULL
conf_type *config_ds_get_typename(const char *typename) {
  int i;
  conf_type *tmp;
  for (i = 0; i < MAX_TYPES; i++) {
    tmp = snmprdbms_ds_get_void(i);
    if ( (tmp == SNMPRDBMS_ERROR) || (tmp == NULL) )
      return NULL;
    else
      if ( strcmp(typename, tmp->typeName) == 0 )
        return tmp;
  }
  return NULL;
}

//store type name in DS, return NULL if fail
static conf_type *config_ds_set_typename(const char *typename) {
  int i;
  conf_type *tmp;

  if ( config_ds_get_typename(typename) == NULL ) {

    for (i = 0; i < MAX_TYPES; i++) //last free slot in DS
      if ( snmprdbms_ds_get_void(i) == NULL )
        break;

      //if no in DS then add new typename
      tmp = malloc(sizeof(conf_type));
      tmp->typeName = strdup(typename);
      if( snmprdbms_ds_set_void(i, tmp) == SNMPRDBMS_SUCCESS )
        return tmp;
      else
        return NULL;
  }
  return config_ds_get_typename(typename);
}

//conf_type *type MUST be allocated!!!
static char **config_get_param_ptr(const char *paramname, conf_type *type) {
#define  ifparam(x) if ( strcmp(paramname, x) == 0 )
  ifparam("rdbmsDbTable") return &(type->rdbmsDbTable);
  ifparam("rdbmsDbInfoTable") return &(type->rdbmsDbInfoTable);
  ifparam("rdbmsDbParamTable") return &(type->rdbmsDbParamTable);
  ifparam("rdbmsDbLimitedResourceTable") return &(type->rdbmsDbLimitedResourceTable);
  ifparam("rdbmsSrvTable") return &(type->rdbmsSrvTable);
  ifparam("rdbmsSrvInfoTable") return &(type->rdbmsSrvInfoTable);
  ifparam("rdbmsSrvParamTable") return &(type->rdbmsSrvParamTable);
  ifparam("rdbmsSrvLimitedResourceTable") return &(type->rdbmsSrvLimitedResourceTable);
  ifparam("rdbmsRelTable") return &(type->rdbmsRelTable);
  return NULL;
}


//return query or NULL
char *config_ds_get_param(const char *typename, const char *paramname) {

  conf_type *type = config_ds_get_typename(typename);
  if (type == NULL)
    return NULL;

   return *config_get_param_ptr(paramname, type);
}

//return type or NULL
static conf_type *config_ds_set_param(const char *typename, const char *paramname, const char *paramval) {
  conf_type *type = config_ds_get_typename(typename);
  if (type == NULL)
    return NULL;

  char **param_ptr = config_get_param_ptr(paramname, type);
  if (param_ptr == NULL)
    return NULL;

  (*param_ptr) = strdup( paramval );

  return type;
}

static void parse_define_token(char *line) {

    char *tmp, *typename = NULL, *paramname = NULL, *paramval = NULL;
    int token_in_line = 0;

    tmp = strtok(line, " \t");
    while (tmp != NULL) {

        switch (token_in_line++) {

        //type name
        case 0:
          typename = tmp;
          if ( config_ds_get_typename(typename) == NULL ) {
          if ( config_ds_set_typename(typename) != NULL )
            debug("Type \"%s\":\n", typename);
          else
            debug("Error with type \"%s\":\n", typename);
          }

          tmp = strtok(NULL, " \t");
          break;

        //param name
        case 1:
          paramname = tmp;
          tmp = strtok(NULL, "\n\r\v\f");
          break;

        //param value
        default:
          paramval = tmp;
          if ( config_ds_set_param(typename, paramname, paramval) != NULL )
            debug(" \t%s@%s = %s\n", config_ds_get_typename(typename)->typeName, paramname, config_ds_get_param(typename, paramname));
          else
            debug(" Error with \t%s@%s = %s\n", typename, paramname, paramval);

          tmp = NULL;
          break;

        }

    }

}

//============= INSTANCES ==============

conf_instance_param *prev_instance = NULL;
conf_instance_param *first_instance = NULL;

//create new instance with id or return already exist
static conf_instance_param *
config_ds_set_instance(unsigned int id) {
  conf_instance_param *instance = snmprdbms_ds_get_void(MAX_TYPES + id);
  if ( (instance != 0) && (instance != NULL) && (instance != SNMPRDBMS_ERROR) )
    return instance;

  instance = malloc( sizeof(conf_instance_param) );
  if (instance == NULL) {
    debug("Error with instance %d\n", id);
    return NULL;
  }

  instance->instance_id = id;
  
  if (first_instance == NULL)
    first_instance = instance;

  if (prev_instance != NULL)
    prev_instance->next = instance;

  instance->next = NULL;

  prev_instance = instance;

  if ( snmprdbms_ds_set_void(MAX_TYPES + id, instance) == SNMPRDBMS_SUCCESS )
    return instance;
  else
    return NULL;
}

//return instance or NULL; if id == -1, return first instance
conf_instance_param *
config_ds_get_instance(int id) {
  if (id == -1)
    return first_instance;
  return snmprdbms_ds_get_void(MAX_TYPES + id);
}

//return param value of instance with ID or NULL
void **config_get_param_ptr2(char* paramname, unsigned int id) {
#define ifparam2(x) if ( strcmp(paramname, x) == 0 )

  conf_instance_param *instance = config_ds_get_instance(id);
  if (instance == NULL)
    return NULL;

  ifparam2(CONF_KW_CSTRING) return (void **)&(instance->cstring);
  ifparam2(CONF_KW_TYPE) return (void **)&(instance->type);

  return NULL;

}

char *config_ds_get_instance_param(unsigned int id, char *paramname) {
  return (*config_get_param_ptr2(paramname, id));
}

static conf_instance_param *
config_ds_set_instance_param(char *paramname, char *paramvalue, unsigned int instanceid) {

  char **param_ptr;

  param_ptr = (char **)config_get_param_ptr2(paramname, instanceid);
  if (param_ptr == NULL)
    return NULL;

  *param_ptr = strdup(paramvalue);

  return config_ds_get_instance(instanceid);

}

char *conf_ds_get_instance_type_param(char *paramname, unsigned int instanceid) {
  conf_instance_param *instance = config_ds_get_instance(instanceid);
  if ( (instance == NULL) || (instance->type == NULL) )
    return NULL;

  return config_ds_get_param(instance->type->typeName, paramname);
}

static void parse_instance_token(char *line ) {
  char *tmp, *typename = NULL, *paramname = NULL, *paramvalue = NULL;
  int token_in_line = 0, instanceid = 0;
  conf_instance_param *ip;
  void **param_ptr = NULL;
  int error_in_line = 0;


  ip = malloc(sizeof(conf_instance_param));

  tmp = strtok(line, " \t");
  while (tmp != NULL) {

    switch (token_in_line++) {

    //instanceid
    case 0:
      instanceid = atoi(tmp);
      ip = config_ds_set_instance(instanceid);
      if (ip == NULL)
        debug("Error with instance ID = %d:\n", instanceid);
      else {
        int debug_instance_id_displayed = snmprdbms_ds_get_int(MAX_TYPES + MAX_INSTANCES);
        if ( debug_instance_id_displayed != instanceid ) {
          //debug message with instance id was displayed
          if ( snmprdbms_ds_set_int(MAX_TYPES + MAX_INSTANCES, instanceid) == SNMPRDBMS_ERROR ) {
            debug("Error on  %s:%d\n", __FILE__, __LINE__ - 1);
            break;
          }
          debug("Instance ID = %d:\n", instanceid);
        }
      }
      tmp = strtok(NULL, " \t");
      break;

    //param name
    case 1:
      paramname = tmp;
      param_ptr = config_get_param_ptr2(paramname, instanceid);
      if (param_ptr == NULL) {
        error_in_line = 1;
        debug(" \tUnknown config param \"%s\"\n", paramname);
      }
      tmp = strtok(NULL, "\n\r\v\f");
      break;

    //instance param value
    case 2:
      if (error_in_line != 0)
        break;

      paramvalue = tmp;

      if ( strcmp(CONF_KW_TYPE, paramname) == 0 ) { //param "type"
        typename = paramvalue;

        conf_type *type = config_ds_get_typename(typename);
        if ( type == NULL )
          error_in_line = 2;
        else
          (*param_ptr) = type;

        debug( " \tTYPE = %s%s\n", typename, (error_in_line > 0)?"(undefined!)":"" );

      } else {

      ip = config_ds_set_instance_param(paramname, paramvalue, instanceid);
      if (ip != SNMPRDBMS_ERROR)
        debug(" \t%s = %s\n", paramname, *(param_ptr) );
      else
        debug("Error with instance ID = %d and param %s\n", instanceid, paramname);
      }
      tmp = NULL;
      break;

    }

  }

}

void parse_config(const char *token, char *line) {

  if ( strcmp(token, CONF_DEFINE) == 0 ) {
    parse_define_token(line);
  } else if ( strcmp(token, CONF_INSTANCE) == 0 )
    parse_instance_token(line);

}

//free DS before re-read config
void free_config(void) {

}

